package com.palmcity.rtti.excode;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.palmcity.rtti.base.DTICity;
import com.palmcity.rtti.base.DTIEvent;
import com.palmcity.rtti.base.DTIJam;
import com.palmcity.rtti.base.DTILink;
import com.palmcity.rtti.base.DTIMesh;
import com.palmcity.rtti.base.EventLocation;
import com.palmcity.rtti.base.EventTime;
import com.palmcity.rtti.base.PassingRoad;
import com.palmcity.rtti.util.TrafficUtils;

/**
 * <p>
 * DTINEWExcoding
 * </p>
 * <p>
 * DTI1.5单个城市数据解析类
 * </p>
 *
 * @author 周华彬(zhouhuabin@ctfo.com, zhou_hua_bin@163.com)
 * @version 0.0.0
 *          <table style="border:1px solid gray;">
 *          <tr>
 *          <th width="100px">版本号</th>
 *          <th width="100px">动作</th>
 *          <th width="100px">修改人</th>
 *          <th width="100px">修改时间</th>
 *          </tr>
 *          <!-- 以 Table 方式书写修改历史 -->
 *          <tr>
 *          <td>0.0.0</td>
 *          <td>创建类</td>
 *          <td>zhouhuabin</td>
 *          <td>2015年1月5日 上午10:05:42</td>
 *          </tr>
 *          <tr>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          </tr>
 *          </table>
 */
public class Dti15Decoder {
	public static final String PUBLISH_DTI_CHARSET_GBK = "GBK";

	private static Logger log = Logger.getLogger(Dti15Decoder.class);

	/** dti发布数据b2c格式的起始位置 */
	public static final int PUBLISH_DTI_STARTPOS_B2C = 68;
	/** dti发布数据b2b格式的起始位置 */
	public static final int PUBLISH_DTI_STARTPOS_B2B = 0;
	/** 实时路况 */
	private static final byte dtiJamSign = 1;
	/** 预测路况 */
	private static final byte dtiPredictJamSign = 3;

	/**
	 * 解析DTI发布时间
	 * @param timeData
	 * @param dtiCity
	 */
	private void parseDtiPublishTime(List<Byte> timeData, DTICity dtiCity) {
		try {
			/** * 信息分割符号 */
			int infoSeg = timeData.get(0);
			/** * 数据头参数 */
			int contentHeadSeg = timeData.get(1);
			/** * 节目编号 */
			int programID = timeData.get(2);
			/** * 项更新标识 */
			int conUpdateFlag = (timeData.get(3) >> 6);
			/** * 内容项编号 */
			int contentID = (timeData.get(3) & 0x003f);
			String contentMessage = null;
			if (contentID == 0) {
				contentMessage = "节目头";
			} else if (contentID == 1) {
				contentMessage = "道路交通状况和旅行时间";
			} else if (contentID == 2) {
				contentMessage = "交通管制和事件信息";
			} else if (contentID == 3) {
				contentMessage = "停车诱导信息";
			} else {
				contentMessage = "未定义";
			}

			/** * 内容项分类 */
			int conCategory = timeData.get(4) >> 4;
			String conCategoryMessage = null;
			if (conCategory == 0) {
				conCategoryMessage = "无指定";
			} else if (conCategory == 1) {
				conCategoryMessage = "道路交通状况和旅行时间";
			} else if (conCategory == 2) {
				conCategoryMessage = "交通管制和事件信息";
			} else if (conCategory == 3) {
				conCategoryMessage = "停车诱导信息";
			} else {
				conCategoryMessage = "未定义";
			}
			/** * 信息发布时间小时的高三位 */
			int highHH = timeData.get(4) & 0x07;
			/** * 信息发布时间小时的低两位 */
			int lowHH = (timeData.get(5) & 0xc0) >>> 6;
			/** * 信息发布时间分钟 */
			int TT = timeData.get(5) & 0x3f;

			int HH = (highHH << 2) | (lowHH & 0x03);
			/**
			 * 计算timeid: 用1-2880表示一天24小时，五分钟一个间隔
			 */
			if (TT % 5 == 0) {
				TT = (int) (TT / 0.5);
			} else {
				TT = (int) (TT / 0.5 + 1);
			}
			int timeId = (int) (HH * (60 / 0.5) + Math.ceil(TT));
			dtiCity.setTimeId(timeId);
		} catch (Exception e) {
			log.error("解析DTI发布时间异常,msg=" + e.getLocalizedMessage(), e);
		}
	}

	/**
	 * 解析数据文件中的路况和平均行驶速度
	 * 
	 * @param jamSegmentData
	 * @param i
	 * @param linkNum
	 * @param dtiMesh
	 * @param timekind
	 * @param startLinkID
	 * @param roadClass
	 * @param output
	 * @return
	 */
	private int parseDtiJamWithSpeedData(List<Byte> jamSegmentData, int i, int linkNum, DTIMesh dtiMesh, int timekind, int startLinkID, int roadClass) {
		int cur = i;
		for (int pos = 0; pos < linkNum; pos++) {
			try {
				DTIJam dtiJam = new DTIJam();
				if (timekind == 0) {
					dtiJam.setInfoType(dtiJamSign);
				} else {
					dtiJam.setInfoType(dtiPredictJamSign);
				}

				byte info = jamSegmentData.get(cur++);
				DTILink link = new DTILink();
				link.setLinkId((short) (startLinkID + pos));
				link.setRoadClass((short) (roadClass));
//				link.setRoadClass((short) (TrafficUtils.getRealRoadClass(roadClass)));
				link.setMeshId(dtiMesh.getMeshId());
				dtiJam.setLink(link);
				if (((info & 0xff) >>> 6) != 0 || (info & 0x3f) != 0) {
					dtiJam.setCondition(TrafficUtils.getRoadCondition((info & 0xff) >>> 6));
					int extendFlag = ((info & 0x38) >>> 3);

					if (extendFlag == 1) {
						dtiJam.setSpeed(getAvgSpeed(info));
					} else if (extendFlag == 2) {
						dtiJam.setSpeed(getAvgSpeed(info));

						/** TODO BUG 扩展信息1 目前扩展信息没有用到 */
						int firExtentInfo1 = jamSegmentData.get(cur++);
						int secExtentInfo1 = jamSegmentData.get(cur++);

					} else if (extendFlag == 3) {
						/**
						 * TODO BUG 扩展信息1 目前扩展信息没有用到
						 */
						int firExtentInfo1 = jamSegmentData.get(cur++);
						int secExtentInfo1 = jamSegmentData.get(cur++);
						/** 扩展信息2 */
						int extentInfo2 = jamSegmentData.get(cur++);
						dtiJam.setSpeed(getAvgSpeed(info, extentInfo2));

					} else if (extendFlag == 4) {
						/** 扩展信息2 */
						int extentInfo2 = jamSegmentData.get(cur++);
						dtiJam.setSpeed(getAvgSpeed(info, extentInfo2));
					} else {
						int meshid = dtiJam.getLink().getMeshId();
						short roadGrade = dtiJam.getLink().getRoadClass();
						int linkid = dtiJam.getLink().getLinkId();
					}
					dtiMesh.addDTI(dtiJam);
				}
			} catch (Exception e) {
				log.error("解析DTIJam对象异常,msg=" + e.getLocalizedMessage(), e);
			}
		}
		return cur;
	}

	/**
	 * 解析平均旅行时间，数据结构中有扩展信息2时用
	 * 
	 * @param baseByte
	 *            速度基本字节
	 * @param extentByte
	 *            速度扩展字节
	 * @return
	 */
	private float getAvgSpeed(byte baseByte, int extentByte) {
		return (((extentByte & 0xff) << 3) | (baseByte & 0x07)) * 0.1f;
	}

	/**
	 * 解析平均旅行时间 ,数据结构中无扩展信息2时用
	 * 
	 * @param baseByte
	 * @return
	 */
	private float getAvgSpeed(byte baseByte) {
		return (baseByte & 0x07) * 0.1f;
	}

	/**
	 * 解析路况和事件
	 * 
	 * @param dtiSegmentByte
	 *            要解析的字节数组
	 * @param dtiMesh
	 *            要存放的mesh对象
	 */
	private void parseDtiSegment(List<Byte> dtiSegmentByte, DTIMesh dtiMesh) {
		try {
			int i = 0;
			/**
			 * 信息分割符
			 */
			int infoSeg = dtiSegmentByte.get(0);
			/**
			 * 数据段参数
			 */
			int DataSeg = dtiSegmentByte.get(1);
			/**
			 * 数据关联标识
			 */
			int DSL = (dtiSegmentByte.get(2) & 0x80) >>> 7;
			String isRelated = null;
			if (DSL == 0) {
				isRelated = "不与后续数据段关联";
			} else if (DSL == 1) {
				isRelated = "与后续数据段关联";
			} else {
				isRelated = "数据段关联标识出错 ";
			}
			/**
			 * 数据段容量高 7 位
			 */
			int highDataLength = dtiSegmentByte.get(2) & 0x7f;
			/**
			 * 数据段容量低 8 位
			 */
			int lowDataLength = dtiSegmentByte.get(3);
			int dataLength = (highDataLength << 8 & 0x7f00) | (lowDataLength & 0xff);
			i += 4;
			while (i < (dataLength + 4)) {
				/**
				 * 解析动态交通信息，
				 */
				if (DataSeg == 0x40) {

					byte ctb0 = dtiSegmentByte.get(i++);
					/**
					 * 信息类型
					 */
					int infoType = (ctb0 & 0x80) >>> 7;
					/**
					 * 旅行时间类别
					 */
					int timeKind = (ctb0 & 0x40) >>> 6;
					/**
					 * 信息数标识
					 */
					int infoNum = (ctb0 & 0x20) >>> 5;

					/**
					 * 连续路链数高 4 位
					 */
					int highLinkNum = ctb0 & 0x0f;
					/**
					 * 连续路链数 低 8 位
					 */
					int lowLinkNum = dtiSegmentByte.get(i++);
					int linkNum = (highLinkNum << 8 & 0x0f00) | (lowLinkNum & 0xff);
					int roadClass = 0;
					int startLinkID = 0;
					try {
						int ctb2 = dtiSegmentByte.get(i++);
						roadClass = (ctb2 & 0x70) >>> 4;
						int highStartLinkID = ctb2 & 0x0f;
						int lowStartLinkID = dtiSegmentByte.get(i++);
						startLinkID = (highStartLinkID << 8 & 0x0f00) | (lowStartLinkID & 0xff);
					} catch (Exception e) {
						log.error("解析路链对象异常meshid=" + dtiMesh.getMeshId() + ",msg=" + e.getLocalizedMessage(), e);
					}
					if (infoType == 0) {// 带速度的路况
						i = parseDtiJamWithSpeedData(dtiSegmentByte, i, linkNum, dtiMesh, timeKind, startLinkID, roadClass);
					} else if (infoType == 1) {
						log.error("DTI1.0解析路链暂时不支持,meshid=" + dtiMesh.getMeshId());
					}
				}
				/**
				 * 解析事件和交通管制信息
				 */
				else if (DataSeg == 0x41) {
					int re1b0 = dtiSegmentByte.get(i++);
					/** 信息扩展标识 */
					int extentSign = (re1b0 & 0xc0) >>> 6;
					int linkNum = re1b0 & 0x3f;
					EventLocation eventLocation = new EventLocation();
					DTIEvent dtiEvent = new DTIEvent();
					int pos = 1;
					eventLocation.setAffectLinks(linkNum);

					int re1b1 = dtiSegmentByte.get(i++);
					int eventType = (((re1b1 & 0xf0) >>> 4) + 256) % 256;
					int controlType = ((re1b1 & 0x0f) + 256) % 256;
					int re1b2 = dtiSegmentByte.get(i++);
					int unit = (re1b2 & 0xc0) >>> 6;
					int controlLength = re1b2 & 0x3f;
					eventLocation.setTotalDistance(controlLength);
					eventLocation.setUnit(unit);
					dtiEvent.setEventCtlType(controlType);
					dtiEvent.setEventType(eventType);

					while (pos <= linkNum) {
						/** * 起始路链信息 */
						if (pos == 1) {
							int re1b3 = dtiSegmentByte.get(i++);
							int meshSign = (re1b3 & 0x80) >>> 7;
							int nameSign = (re1b3 & 0x40) >>> 6;
							int highLinkID = re1b3 & 0x0f;
							int lowLinkID = dtiSegmentByte.get(i++);
							int LinkID = (highLinkID << 8 & 0x0f00) | (lowLinkID & 0xff);
							int roadClass = dtiSegmentByte.get(i++) & 0x07;
							if (nameSign == 1) {
								int nameLength = dtiSegmentByte.get(i++) & 0xff;
								byte[] nameBytes = new byte[nameLength];
								for (int m = 0; m < nameLength; m++) {
									nameBytes[m] = dtiSegmentByte.get(i++);
								}
								try {
									String startName = new String(nameBytes, PUBLISH_DTI_CHARSET_GBK);
									eventLocation.setStartName(startName);
								} catch (UnsupportedEncodingException e) {
									log.error("解析路链名称异常,msg=" + e.getLocalizedMessage(), e);
								}
							}

							DTILink startLink = new DTILink();
							startLink.setLinkId((short) LinkID);
							startLink.setRoadClass((short) roadClass);
							startLink.setMeshId(dtiMesh.getMeshId());
							eventLocation.setStartLink(startLink);
							pos++;
						}

						/*** 终止路链信息 */
						else if (pos == 2) {
							int re1b3 = dtiSegmentByte.get(i++);
							int meshSign = (re1b3 & 0x80) >>> 7;
							int nameSign = (re1b3 & 0x40) >>> 6;
							int highLinkID = re1b3 & 0x0f;
							int lowLinkID = dtiSegmentByte.get(i++);
							int endLinkID = (highLinkID << 8 & 0x0f00) | (lowLinkID & 0xff);
							int roadClass = dtiSegmentByte.get(i++) & 0x07;

							DTILink endLink = new DTILink();
							endLink.setLinkId((short) endLinkID);
							endLink.setRoadClass((short) roadClass);
							if (meshSign == 1) {
								int meshid = TrafficUtils.getGridID(dtiSegmentByte, i);
								endLink.setMeshId(meshid);
								i += 3;
							} else {
								endLink.setMeshId(dtiMesh.getMeshId());
							}
							eventLocation.setEndLink(endLink);
							if (nameSign == 1) {
								int nameLength = dtiSegmentByte.get(i++) & 0xff;
								byte[] nameBytes = new byte[nameLength];
								for (int m = 0; m < nameLength; m++) {
									nameBytes[m] = dtiSegmentByte.get(i++);
								}
								try {
									String endName = new String(nameBytes, PUBLISH_DTI_CHARSET_GBK);
									eventLocation.setEndName(endName);
								} catch (UnsupportedEncodingException e) {
									log.error("解析终止路链名称异常,msg=" + e.getLocalizedMessage(), e);
								}
							}
							pos++;
						}

						/**
						 * 途径路链信息
						 */
						else if (pos <= linkNum) {
							int re1b3 = dtiSegmentByte.get(i++);
							int meshSign = (re1b3 & 0x80) >>> 7;
							int nameSign = (re1b3 & 0x40) >>> 6;
							int highLinkID = re1b3 & 0x0f;
							int lowLinkID = dtiSegmentByte.get(i++);
							int passLinkID = (highLinkID << 8 & 0x0f00) | (lowLinkID & 0xff);
							int roadClass = dtiSegmentByte.get(i++) & 0x07;
							int continusLinkNum = dtiSegmentByte.get(i++);
							int meshid = 0;

							/**
							 * 如果途径路链和起始路链不在一个图幅内则解析途径路链的图幅号信息，否则用起始路链的图幅号
							 */
							if (meshSign == 1) {
								meshid = TrafficUtils.getGridID(dtiSegmentByte, i);
								i += 3;
							} else {
								meshid = dtiMesh.getMeshId();
							}
							for (int passings = 0; passings < continusLinkNum; passings++) {
								DTILink passLink = new DTILink();
								passLink.setMeshId(meshid);
								passLink.setLinkId((short) (passLinkID + passings));
								passLink.setRoadClass((short) roadClass);
								PassingRoad passingRoad = new PassingRoad();
								passingRoad.setLink(passLink);
								if (nameSign == 1) {
									int nameLength = dtiSegmentByte.get(i++) & 0xff;
									byte[] nameBytes = new byte[nameLength];
									for (int m = 0; m < nameLength; m++) {
										nameBytes[m] = dtiSegmentByte.get(i++);
									}
									try {
										String passName = new String(nameBytes, PUBLISH_DTI_CHARSET_GBK);
										passingRoad.setName(passName);
									} catch (UnsupportedEncodingException e) {
										log.error("解析途经路链名称异常,msg=" + e.getLocalizedMessage(), e);
									}

								}
								pos++;
								eventLocation.addPassRoad(passingRoad);
							}
						}
					}

					/**
					 * 对扩展信息 1 进行解码
					 */
					if (extentSign >= 1) {
						int eventCtrlsType = (dtiSegmentByte.get(i++) + 256) % 256;
						int DTIEventsType = (dtiSegmentByte.get(i++) + 256) % 256;
						byte REEB2 = dtiSegmentByte.get(i++);
						byte REEB3 = dtiSegmentByte.get(i++);
						int disUnit = (REEB2 >>> 6 & 0x02) | (REEB3 >>> 7 & 0x01);
						int startDistance = REEB2 & 0x7f;
						int endDistance = REEB3 & 0x7f;
						eventLocation.setStartDistance(startDistance);
						eventLocation.setEndDistance(endDistance);
						eventLocation.setUnit(disUnit);
						dtiEvent.setEventCtlSType(eventCtrlsType);
						dtiEvent.setEventSType(DTIEventsType);
					}
					dtiEvent.setEventLocation(eventLocation);
					/** * 对扩展信息 2 进行解码 */
					if (extentSign >= 2) {
						int timeType = dtiSegmentByte.get(i++) >>> 7 & 0x01;
						int REB5 = dtiSegmentByte.get(i++);
						int REB6 = dtiSegmentByte.get(i++);
						int startYYYY = (REB5 << 4 | (REB6 >>> 4 & 0x0F)) & 0x0FFF;
						int endYYYY = startYYYY + (REB6 & 0x0F);
						int REEB7 = dtiSegmentByte.get(i++);
						int startMM = REEB7 >>> 4 & 0x0f;
						int endMM = REEB7 & 0x0f;
						int REEB8 = dtiSegmentByte.get(i++);
						int startDD = REEB8 >>> 3 & 0x1f;
						int startHHHigh3 = REEB8 & 0x07;
						int REEB9 = dtiSegmentByte.get(i++);
						int startHHLow2 = REEB9 >>> 6 & 0x03;
						int startHH = (startHHHigh3 << 2 & 0x1f) | (startHHLow2 & 0x03);
						int startTT = REEB9 & 0x3f;
						int REEB10 = dtiSegmentByte.get(i++);
						int endDD = REEB10 >>> 3 & 0x1f;
						int endHHHigh3 = REEB10 & 0x07;
						int REEB11 = dtiSegmentByte.get(i++);
						int endHHLow2 = REEB11 >>> 6 & 0x03;
						int endHH = (endHHHigh3 << 2 & 0x1f) | (endHHLow2 & 0x03);
						int endTT = REEB11 & 0x3f;
						EventTime eventTime = new EventTime();
						eventTime.setEndYYYY(endYYYY);
						eventTime.setEndDD(endDD);
						eventTime.setEndHH(endHH);
						eventTime.setEndMM(endMM);
						eventTime.setEndTT(endTT);
						eventTime.setStartYYYY(startYYYY);
						eventTime.setStartDD(startDD);
						eventTime.setStartHH(startHH);
						eventTime.setStartMM(startMM);
						eventTime.setStartTT(startTT);
						eventTime.setTimeType(timeType);
						dtiEvent.setEventTime(eventTime);
					}

					/**
					 * 对扩展信息 3 进行解码
					 */
					if (extentSign == 3) {
						int m = 0;
						int lon = 0;
						for (int j = 0; j < 4; j++) {
							lon = lon | ((dtiSegmentByte.get(i++) & 0xFF) << 8 * j);
						}
						eventLocation.setLon(lon);
						int lat = 0;
						for (int j = 0; j < 4; j++) {
							lat = lat | ((dtiSegmentByte.get(i++) & 0xFF) << 8 * j);
						}
						eventLocation.setLat(lat);
						int remarkELength = dtiSegmentByte.get(i++) & 0xFF;
						if (remarkELength != 0) {
							if (remarkELength < 0) {
								remarkELength += 256;
							}
							byte[] remarkEBytes = new byte[remarkELength];

							for (; m < remarkELength; m++) {
								remarkEBytes[m] = dtiSegmentByte.get(i++);
							}
							m = 0;
							try {
								String remarkE = new String(remarkEBytes, PUBLISH_DTI_CHARSET_GBK);
								dtiEvent.setRemarkEvent(remarkE);
							} catch (UnsupportedEncodingException e) {
								log.error("解析事件描述异常,msg=" + e.getLocalizedMessage(), e);
							}

						}

						int remarkCLength = dtiSegmentByte.get(i++) & 0xFF;
						if (remarkCLength != 0) {
							if (remarkELength < 0) {
								remarkELength += 256;
							}
							byte[] remarkCBytes = new byte[remarkCLength];
							for (; m < remarkCLength; m++) {
								remarkCBytes[m] = dtiSegmentByte.get(i++);
							}
							m = 0;
							try {
								String remarkC = new String(remarkCBytes, PUBLISH_DTI_CHARSET_GBK);
								dtiEvent.setRemarkControl(remarkC);
							} catch (UnsupportedEncodingException e) {
								log.error("解析管制描述异常,msg=" + e.getLocalizedMessage(), e);
							}
						}
					}
					dtiMesh.addDTI(dtiEvent);
				}

				/**
				 * 解析停车诱导信息
				 */
				else if (DataSeg == 0x42) {
					log.error("暂时不支持停车诱导对象解析.");
				}
			}
		} catch (Exception e) {
			log.error("解析路链对象异常,msg=" + e.getLocalizedMessage(), e);
		}
	}

	/**
	 * dti对象解析入口方法
	 * @param bitData
	 * @param cityCode
	 * @return
	 */
	public DTICity excodeCity(byte[] bitData, String cityCode) {
		if (bitData == null || bitData.length == 0) {
			return null;
		}
		int index = 0;
		DTICity sinCityData = new DTICity();
		sinCityData.setCityCode(cityCode);
		for (int i = 0; i < bitData.length;) {
			// 解析网格字节
			if (bitData[i] == 0x1E && bitData[i + 1] == 0x31) {
				List<Byte> listByteProgram = new ArrayList<Byte>();
				DTIMesh dtiMesh = new DTIMesh();
				sinCityData.addMesh(dtiMesh);
				index = sinCityData.getMeshList().indexOf(dtiMesh);
				int cur = 0;
				while (i != bitData.length && cur != 11) {
					listByteProgram.add(bitData[i]);
					i++;
					cur++;
				}
				if (listByteProgram.size() == 11) {
					int meshid = TrafficUtils.getGridID(listByteProgram, 8);
					dtiMesh.setMeshId(meshid);
				}
			} // 解析路况时间
			else if (bitData[i] == 0x1E && bitData[i + 1] == 0x33) {
				List<Byte> listByteContent = new ArrayList<Byte>();
				int cur = 0;
				while (i != bitData.length && cur != 6) {
					listByteContent.add(bitData[i]);
					i++;
					cur++;
				}
				if (listByteContent.size() == 6) {
					parseDtiPublishTime(listByteContent, sinCityData);
				}
			} // 解析交通路况和事件
			else if (bitData[i] == 0x1F && (bitData[i + 1] == 0x40 | bitData[i + 1] == 0x41 | bitData[i + 1] == 0x42 | bitData[i + 1] == 0x43)) {
				List<Byte> listByteDataSeg = new ArrayList<Byte>();
				int highDataLength = bitData[i + 2] & 0x7f;
				/** * 数据段容量低 8 位 */
				int lowDataLength = bitData[i + 3];
				
				int dataLength = (highDataLength << 8 & 0x7f00) | (lowDataLength & 0xff);
				for (int footStep = 0; footStep < (dataLength + 4); footStep++) {
					listByteDataSeg.add(bitData[i + footStep]);
				}
				i = i + dataLength + 4;
				parseDtiSegment(listByteDataSeg, sinCityData.getMeshList().get(index));
			}
		}
		return sinCityData;
	}
}
